package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.constant.HttpStatus;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.ServiceException;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.nursing.domain.dto.DeviceDto;
import com.zzyl.nursing.domain.pojo.Device;
import com.zzyl.nursing.domain.vo.DeviceDetailVo;
import com.zzyl.nursing.domain.vo.ProductVo;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IDeviceService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 设备Service业务层处理
 * @author alexis
 * @date 2025-05-28
 */
@Service
@RequiredArgsConstructor
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {
    private final DeviceMapper deviceMapper;
    private final IoTDAClient ioTDAClient;
    private final StringRedisTemplate stringRedisTemplate;
    
    /**
     * 查询设备列表
     * @param device 设备
     * @return 设备
     */
    @Override
    public List<Device> selectDeviceList(Device device) {
        return deviceMapper.selectDeviceList(device);
    }
    
    /**
     * 新增设备
     * @param device 设备
     * @return 结果
     */
    @Override
    public int insertDevice(Device device) {
        return deviceMapper.insert(device);
    }
    
    /**
     * 修改设备
     * @param device 设备
     * @return 结果
     */
    @Override
    public int updateDevice(Device device) {
        return deviceMapper.updateById(device);
    }
    
    /**
     * 批量删除设备
     * @param ids 需要删除的设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceByIds(Long[] ids) {
        return deviceMapper.deleteBatchIds(Arrays.asList(ids));
    }
    
    /**
     * 删除设备信息
     * @param id 设备主键
     * @return 结果
     */
    @Override
    public int deleteDeviceById(Long id) {
        return deviceMapper.deleteById(id);
    }
    
    @Override
    public void syncProductList() {
        // 1-查询iot中所有产品
        ListProductsRequest request=new ListProductsRequest();
        ListProductsResponse listProductsResponse=ioTDAClient.listProducts(request);
        // 2-将所有产品存入到redis中
        if (listProductsResponse.getHttpStatusCode() != 200) {
            throw new ServiceException("调用iot平台接口失败");
        }
        List<ProductSummary> products=listProductsResponse.getProducts();
        stringRedisTemplate.opsForValue()
                           .set(CacheConstants.ALL_PRODUCT_KEY,JSONUtil.toJsonStr(products));
    }
    
    @Override
    public List<ProductVo> allProduct() {
        // 1-从redis获取
        String resultJson=stringRedisTemplate.opsForValue()
                                             .get(CacheConstants.ALL_PRODUCT_KEY);
        // 2-转换结果：List<ProductVo>
        return JSONUtil.toList(resultJson,ProductVo.class);
    }
    
    @Override
    public void registerDevice(DeviceDto deviceDto) {
        // 1-校验：去表中查询是否存在
        // 校验设备名称
        Long deviceCount=this.lambdaQuery()
                             .eq(Device::getDeviceName,deviceDto.getDeviceName())
                             .count();
        if (deviceCount > 0) {
            throw new ServiceException("设备名称重复");
        }
        // 校验设备标识码
        Long nodeIdCount=this.lambdaQuery()
                             .eq(Device::getNodeId,deviceDto.getNodeId())
                             .count();
        if (nodeIdCount > 0) {
            throw new ServiceException("设备标识名称重复");
        }
        // 校验同一个位置不能绑定同一个产品
        Long sameBindCount=this.lambdaQuery()
                               .eq(Device::getBindingLocation,deviceDto.getBindingLocation())
                               .eq(Device::getLocationType,deviceDto.getLocationType())
                               .eq(ObjectUtil.isNotNull(deviceDto.getPhysicalLocationType()),Device::getPhysicalLocationType,deviceDto.getPhysicalLocationType())
                               .eq(Device::getProductKey,deviceDto.getProductKey())
                               .count();
        if (sameBindCount > 0) {
            throw new ServiceException("同一个位置不能绑定同一个产品");
        }
        // 2-注册设备到iot
        AddDeviceRequest request=new AddDeviceRequest();
        AddDevice body=new AddDevice();
        body.withNodeId(deviceDto.getNodeId());
        body.withProductId(deviceDto.getProductKey());
        body.withDeviceName(deviceDto.getDeviceName());
        request.withBody(body);
        AddDeviceResponse addDeviceResponse=ioTDAClient.addDevice(request);
        if (addDeviceResponse.getHttpStatusCode() != 201) {
            throw new ServiceException("iot注册设备失败");
        }
        // 3-把设备保存到表中
        // 设备id
        String deviceId=addDeviceResponse.getDeviceId();
        // 设备秘钥
        String secret=addDeviceResponse.getAuthInfo()
                                       .getSecret();
        Device device=BeanUtil.toBean(deviceDto,Device.class);
        // 给老人或者房间、床分配设备，这里如果是老人随身设备，此时：根据locationType==0，说明是随身设备，则这里物理地址类型就是-1
        device.setPhysicalLocationType(device.getLocationType() == 0 ? -1 : device.getPhysicalLocationType());
        device.setSecret(secret);
        device.setIotId(deviceId);
        this.save(device);
    }
    
    @Override
    public DeviceDetailVo selectDeviceBy(String iotId) {
        // 1-查表
        Device device=this.lambdaQuery()
                          .eq(Device::getIotId,iotId)
                          .one();
        if (ObjectUtil.isNull(device)) {
            return null;
        }
        // 2-iot查询设备
        ShowDeviceRequest request=new ShowDeviceRequest();
        request.withDeviceId(iotId);
        ShowDeviceResponse showDeviceResponse=ioTDAClient.showDevice(request);
        if (showDeviceResponse.getHttpStatusCode() != HttpStatus.SUCCESS) {
            throw new ServiceException("iot查询设备失败");
        }
        // 3-构建返回结果
        DeviceDetailVo deviceDetailVo=BeanUtil.toBean(device,DeviceDetailVo.class);
        // 通过响应对象可以获取：active_time、status、product_nam
        deviceDetailVo.setDeviceStatus(showDeviceResponse.getStatus());
        deviceDetailVo.setProductName(showDeviceResponse.getProductName());
        // 日期时间字符串----转换成--->LocalDateTime
        String activeTime=showDeviceResponse.getActiveTime();
        if (StrUtil.isNotBlank(activeTime)) {
            LocalDateTime activeDateTime=LocalDateTime.parse(activeTime,DateTimeFormatter.ofPattern(DatePattern.UTC_MS_PATTERN));
            activeDateTime=activeDateTime.atZone(ZoneId.from(ZoneOffset.UTC))
                                         .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                                         .toLocalDateTime();
            deviceDetailVo.setActiveTime(activeDateTime);
        }
        return deviceDetailVo;
    }
    
    @Override
    public AjaxResult queryServiceProperties(String iotId) {
        // 1-根据设备id查询设备影子接口：拿到properties对象
        ShowDeviceShadowRequest request=new ShowDeviceShadowRequest();
        request.setDeviceId(iotId);
        ShowDeviceShadowResponse showDeviceShadowResponse=ioTDAClient.showDeviceShadow(request);
        if (showDeviceShadowResponse.getHttpStatusCode() != HttpStatus.SUCCESS) {
            throw new ServiceException("调用iot平台获取设备影子失败");
        }
        // 2-循环遍历properties对象构建List<Map<String，Object>：
        List<DeviceShadowData> shadowList=showDeviceShadowResponse.getShadow();
        // 卫语句：先把异常或者不对的进行判断，剩下就是正常
        if (CollUtil.isEmpty(shadowList)) {
            throw new ServiceException("获取设备不存在物模型数据");
        }
        DeviceShadowProperties reported=shadowList.get(0)
                                                  .getReported();
        if (ObjectUtil.isNull(reported)) {
            throw new ServiceException("获取设备不存在物模型属性");
        }
        // 获取event_time
        LocalDateTime parseLocalDateTime=LocalDateTimeUtil.parse(reported.getEventTime(),"yyyyMMdd'T'HHmmss'Z'");
        LocalDateTime eventTime=parseLocalDateTime.atZone(ZoneId.from(ZoneOffset.UTC))
                                                  .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                                                  .toLocalDateTime();
        // JSONObject 实现Map集合
        JSONObject jsonObject=JSONUtil.parseObj(reported.getProperties());
        List<Map<String, Object>> mapList=new ArrayList<>();
        jsonObject.forEach((k,v) -> {
            Map<String, Object> map=MapUtil.<String, Object>builder()
                                           .put("functionId",k)
                                           .put("value",v)
                                           .put("eventTime",eventTime)
                                           .build();
            mapList.add(map);
        });
        return AjaxResult.success(mapList);
    }
    
    @Override
    public AjaxResult queryProduct(String productKey) {
        // 参数校验
        if (StrUtil.isEmpty(productKey)) {
            throw new BaseException("请输入正确的参数");
        }
        // 调用华为云物联网接口
        ShowProductRequest showProductRequest=new ShowProductRequest();
        showProductRequest.setProductId(productKey);
        ShowProductResponse response;
        
        try {
            response=ioTDAClient.showProduct(showProductRequest);
        } catch (Exception e) {
            throw new BaseException("查询产品详情失败");
        }
        // 判断是否存在服务数据
        List<ServiceCapability> serviceCapabilities=response.getServiceCapabilities();
        if (CollUtil.isEmpty(serviceCapabilities)) {
            return AjaxResult.success(Collections.emptyList());
        }
        
        return AjaxResult.success(serviceCapabilities);
    }
}
